//
//  Student.m
//  RunTime的一些深入
//
//  Created by ZhouFahong on 17/3/30.
//  Copyright © 2017年 ZhouFahong. All rights reserved.
//

#import "Student.h"
#import <objc/runtime.h>
#import "MyClass.h"

@interface Student ()

//@property(strong,nonatomic)NSString *name;

@end

@implementation Student

/*
 在一个函数找不到时，Objective-C提供了三种方式去补救：
 1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数
 
 2、调用forwardingTargetForSelector让别的对象去执行这个函数
 
 3、调用methodSignatureForSelector（函数符号制造器）和forwardInvocation（函数执行器）灵活的将目标函数以其他形式执行。
 如果都不中，调用doesNotRecognizeSelector抛出异常。
 
 */

// 注意，这是C语言函数（不是Objective-C方法）
id name(id self, SEL _cmd) {
    return @"张不坏";
}

// 注意，这是C语言函数（不是Objective-C方法）
void setName(id self, SEL _cmd, id value) {
    NSLog(@"do nothing");
}

// 1.调用resolveInstanceMethod给个机会让类添加这个实现这个函数class_addMethod
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    if (sel == @selector(name)) {
        class_addMethod(self, sel, (IMP)name, "@@:");
        return YES;
    } else if (sel == @selector(setName:)) {
        class_addMethod(self, sel, (IMP)setName, "v@:@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

// 2.调用forwardingTargetForSelector让别的对象去执行这个函数
// 当不能识别方法时候,就会调用这个方法,在这个方法中,我们可以将不能识别的传递给其它对象处理
-(id)forwardingTargetForSelector:(SEL)aSelector{
    
    MyClass *mmm = [[MyClass alloc]init];
//    [mmm performSelector:aSelector];
    return mmm;
}

// 调用methodSignatureForSelector（函数符号制造器）和forwardInvocation（函数执行器）灵活的将目标函数以其他形式执行
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    
    NSString *sel = NSStringFromSelector(aSelector);
    if ([sel rangeOfString:@"set"].location == 0) {
        //动态造一个 setter函数
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    } else {
        //动态造一个 getter函数
        return [NSMethodSignature signatureWithObjCTypes:"@@:"];
    }
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    //拿到函数名
    NSString *key = NSStringFromSelector([anInvocation selector]);
    if ([key rangeOfString:@"set"].location == 0) {
        //setter函数形如 setXXX: 拆掉 set和冒号
        key = [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];
        NSString *obj;
        //从参数列表中找到值
        [anInvocation getArgument:&obj atIndex:2];
        [data setObject:obj forKey:key];
    } else {
        //getter函数就相对简单了，直接把函数名做 key就好了。
        NSString *obj = [data objectForKey:key];
        [anInvocation setReturnValue:&obj];
    }
}

@end
